home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr47
/
dos6mm.zip
/
XMS2EMS.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-03-31
|
63KB
|
1,354 lines
;****************************************************************************
; XMS2EMS is an EMS 3.2 expanded memory manager (EMM) that uses extended
; memory managed by HIMEM.SYS to store data written to expanded memory.
; Its syntax is:
;
; DEVICE=XMS2EMS.SYS [memory] [/H=nnn]
;
; where "memory" is amount of expanded memory to be created (in kilobytes)
; and "nnn" is the number of handles desired (range=8 to 255, default=64).
; If the amount of expanded memory to be made available to the system is not
; specified, XMS2EMS defaults to 256K. The maximum that XMS2EMS supports is
; 8192 (8MB), the minimum 256K.
;
; DEVELOPER'S NOTES:
; ******************
;
; To manage EMS memory, XMS2EMS maintains a table called PAGE_TABLE that
; contains one entry for each 16K EMS page. Each entry is structured as
; follows:
;
; HANDLE DW ?
; LOGICAL_PAGE DW ?
;
; HANDLE is the number of the handle that owns the page. A value of FFFFh
; indicates the page is free. LOGICAL_PAGE is the logical page number for
; the range of memory assigned to the handle. It is only meaningful if
; the HANDLE field contains a valid (non-FFFFh) value.
;
; XMS2EMS also contains one table that holds information about handles.
; HNDL_TABLE contains one 1-byte entry for each handle supported. If the
; byte that corresponds to a handle is 0, then the handle is unassigned.
; This isn't strictly required in EMS 3.2, but it is required for 4.0.
;
; XMS2EMS implements a number of procedures that functions may call upon
; to perform certain often-needed tasks such as verifying that a handle is
; valid or mapping a logical page to a physical page. The procedures are:
;
; GET_FREE_PAGES Return the number of unallocated EMS pages
; GET_FREE_HANDLE Return the number of the next free handle
; ALLOCATE_PAGES Allocate EMS memory and validate the handle
; RELEASE_PAGES Release all EMS pages and invalidate the handle
; CHECK_HANDLE Verify that the handle is valid
; CHECK_SAVE_MAP Search the save area for a specified entry
; GET_PAGE_COUNT Return the number of EMS pages assigned to a handle
; GET_ABS_PAGE Convert logical page number to absolute page number
; MAP_EMS_PAGE Map a logical (absolute) page to a physical page
; RESTORE_PAGE_MAP Restore a mapping context
;
; Calling conventions are documented in the procedure headers.
;****************************************************************************
KEEPBX equ 4
KEEPCX equ 2
KEEPDX equ 1
KEEPNONE equ 0
HARDWARE_ERROR equ 81h ;EMS error returns
INVALID_HANDLE equ 83h
INVALID_FUNCTION equ 84h
NO_MORE_HANDLES equ 85h
TOO_FEW_TOTAL_PAGES equ 87h
TOO_FEW_LOGICAL_PAGES equ 88h
ZERO_PAGES_REQUESTED equ 89h
INVALID_LOGICAL_PAGE equ 8Ah
INVALID_PHYS_PAGE equ 8Bh
SAVE_AREA_FULL equ 8Ch
MAP_ALREADY_SAVED equ 8Dh
MAP_NOT_FOUND equ 8Eh
INVALID_SUBFUNCTION equ 8Fh
code segment
assume cs:code,ds:nothing
org 00h
;////////////////////////////////////////////////////////////////////////////
; Device Driver Header
;////////////////////////////////////////////////////////////////////////////
header dd 0FFFFh ;Pointer to next device
dw 8000h ;Device attribute word
dw offset strat ;Strategy routine address
dw offset intr ;Interrupt routine address
devname db "EMMXXXX0" ;Device name
;////////////////////////////////////////////////////////////////////////////
; Strategy Routine
;////////////////////////////////////////////////////////////////////////////
rqst_header dd ? ;Request header
strat proc far
mov word ptr cs:[rqst_header],bx
mov word ptr cs:[rqst_header+2],es
ret
strat endp
;////////////////////////////////////////////////////////////////////////////
; Interrupt Routine
;////////////////////////////////////////////////////////////////////////////
intr proc far
push ax ;Save registers
push bx
push cx
push dx
push si
push di
push ds
push es
les di,cs:[rqst_header] ;Point ES:DI to request
cmp byte ptr es:[di+2],0 ;header and exit if command
jne intr_exit ;code is not 0
call init ;Initialize the driver
intr_exit: mov word ptr es:[di+3],100h ;"Done" code
pop es ;Restore registers and
pop ds ;return to caller
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
intr endp
;****************************************************************************
; Device Driver Data
;****************************************************************************
ftable dw offset emm_function_01 ;Jump table
dw offset emm_function_02
dw offset emm_function_03
dw offset emm_function_04
dw offset emm_function_05
dw offset emm_function_06
dw offset emm_function_07
dw offset emm_function_08
dw offset emm_function_09
dw offset emm_function_10
dw offset emm_function_11
dw offset emm_function_12
dw offset emm_function_13
dw offset emm_function_14
dw offset emm_function_15
xmm dd ? ;XMS driver entry point
handles dw 64 ;Number of EMM handles
memory dw 256 ;Kilobytes available
pages dw ? ;EMS pages available
page_frame dw ? ;Page frame segment
emb_handle dw ? ;EMB handle
active_handles db 1 ;Active EMS handles
hndl_table_addr dw ? ;Address of handle table
entry_flag db 0 ;0=First call to INT 67h
ems_version db 32h ;EMS version number
page_map_0 dw 0FFFFh ;Array that holds the
page_map_1 dw 0FFFFh ;absolute page numbers
page_map_2 dw 0FFFFh ;mapped to physical
page_map_3 dw 0FFFFh ;pages 0 thru 3
saved_maps db 240h dup (0FFh) ;Page map save area
;****************************************************************************
; INT67H handles calls to the expanded memory manager. The value passed
; back to this routine in BP by the function routines determines what reg-
; isters are popped off the stack. Bit 0 corresponds to DX; bit 1 to CX;
; and bit 2 to BX. If the bit is set, then the corresponding register is
; not restored from the stack.
;****************************************************************************
assume ds:nothing
int67h proc far
sti ;Enable interrupts
cmp ah,40h ;Return error code 84h if
jb invalid_code ;function code is less
cmp ah,4Eh ;than 40h or greater
ja invalid_code ;than 4Eh
push bp ;Save registers
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
cld ;Clear direction flag
push bx ;Save BX
mov bx,cs ;Point DS to this segment
mov ds,bx
assume ds:code
mov es,bx ;Point ES to this segment
cmp entry_flag,0 ;Initialize the driver's
jne int67h_1 ;data area if this is
mov entry_flag,1 ;the first time INT 67h
call init_ems_data ;has been called
int67h_1: sub ah,40h ;Convert function number in
mov bl,ah ;AX to offset address of
xor bh,bh ;handling routine
shl bx,1
mov ax,[offset ftable+bx]
pop bx ;Restore BX
call ax ;Call EMM function
pop es ;Restore registers and exit
pop ds
assume ds:nothing
pop di
pop si
shr bp,1 ;Restore DX if bit in BP
jc popreg1 ;is not set
pop dx
jmp short popreg2
popreg1: add sp,2
popreg2: shr bp,1 ;Restore CX if bit in BP
jc popreg3 ;is not set
pop cx
jmp short popreg4
popreg3: add sp,2
popreg4: shr bp,1 ;Restore BX if bit in BP
jc popreg5 ;is not set
pop bx
jmp short popreg6
popreg5: add sp,2
popreg6: add sp,2 ;Skip AX
pop bp ;Restore BP
iret ;Return to caller
invalid_code: mov ah,INVALID_FUNCTION ;Function not supported
iret
int67h endp
;****************************************************************************
; INIT_EMS_DATA initializes the driver's data space.
;****************************************************************************
assume ds:code
init_ems_data proc near
push ax ;Save registers
push bx
push cx
push di
push es
mov di,offset page_table ;Initialize the page table
mov cx,pages ;by filling it with FFs
shl cx,1
mov ax,0FFFFh
rep stosw
mov di,hndl_table_addr ;Initialize the handle table
mov cx,handles ;by filling it with
xor al,al ;zeroes
rep stosb
mov di,hndl_table_addr ;Mark handle 0 as in use
mov byte ptr [di],1
pop es ;Restore registers and
pop di ;exit
pop cx
pop bx
pop ax
ret
init_ems_data endp
;****************************************************************************
; EMM Function 1 (Get Status)
;****************************************************************************
assume ds:code
emm_function_01 proc near
xor ah,ah ;Zero AH for return
mov bp,KEEPNONE
ret
emm_function_01 endp
;****************************************************************************
; EMM Function 2 (Get Page Frame Segment Address)
;****************************************************************************
assume ds:code
emm_function_02 proc near
mov bx,page_frame ;Place page frame address
xor ah,ah ;in BX and zero AH
mov bp,KEEPBX
ret
emm_function_02 endp
;****************************************************************************
; EMM Function 3 (Get Unallocated Page Count)
;****************************************************************************
assume ds:code
emm_function_03 proc near
call get_free_pages ;Free pages in BX
mov dx,pages ;Total pages in DX
xor ah,ah ;Zero AH for return
mov bp,KEEPBX or KEEPDX
ret
emm_function_03 endp
;****************************************************************************
; EMM Function 4 (Allocate Pages)
;****************************************************************************
assume ds:code
emm_function_04 proc near
mov ah,ZERO_PAGES_REQUESTED ;Error if zero pages were
cmp bx,0 ;requested
je emm4_exit
mov ah,TOO_FEW_TOTAL_PAGES ;Error if request exceeds
cmp bx,pages ;count of total pages
ja emm4_exit
mov dx,bx ;Transfer count
call get_free_pages ;Error if request
mov ah,TOO_FEW_LOGICAL_PAGES ;exceeds count of
cmp dx,bx ;pages free
ja emm4_exit
push dx ;Save page count
call get_free_handle ;Get first free handle
pop cx ;Retrieve page count
mov ah,NO_MORE_HANDLES ;Error if no handles are
jc emm4_exit ;currently available
call allocate_pages ;Allocate the EMS pages
xor ah,ah ;Zero AH for return
emm4_exit: mov bp,KEEPDX
ret
emm_function_04 endp
;****************************************************************************
; EMM Function 5 (Map/Unmap Handle Page)
;
; Note: Logic was added in version 1.1 so that a logical page number
; equal to FFFFh unmaps the corresponding physical page.
;****************************************************************************
assume ds:code
PHYSICAL_PAGE equ byte ptr [bp+18]
LOGICAL_PAGE equ word ptr [bp]
emm_function_05 proc near
push bx ;Save logical page number
mov bp,sp ;Make values addressable
call check_handle ;Error if handle is not
mov ah,INVALID_HANDLE ;valid
jc emm5_exit
mov ah,INVALID_PHYS_PAGE ;Error if physical page
cmp PHYSICAL_PAGE,3 ;number is greater
ja emm5_exit ;than 3
mov bx,0FFFFh ;Set absolute page to FFFFh
cmp LOGICAL_PAGE,0FFFFh ;Branch if the logical page
je emm05_1 ;number is FFFFh
call get_page_count ;Get handle's page count
mov ah,INVALID_LOGICAL_PAGE ;Error if logical page
cmp LOGICAL_PAGE,bx ;number is out of
jae emm5_exit ;range
mov bx,LOGICAL_PAGE ;Compute the absolute page
call get_abs_page ;number
mov bx,ax ;Transfer result to BX
emm05_1: mov al,PHYSICAL_PAGE ;Physical page number in AL
call map_ems_page ;Map the page
mov ah,HARDWARE_ERROR ;Exit on error
jc emm5_exit
xor ah,ah ;Zero AH for return
emm5_exit: mov bp,KEEPNONE ;Set BP
add sp,2 ;Clear the stack
ret
emm_function_05 endp
;****************************************************************************
; EMM Function 6 (Deallocate Pages)
;****************************************************************************
assume ds:code
emm_function_06 proc near
call check_handle ;Error if DX contains an
mov ah,INVALID_HANDLE ;invalid handle
jc emm6_exit
call release_pages ;Deallocate the pages
xor ah,ah ;Zero AH for return
emm6_exit: mov bp,KEEPNONE
ret
emm_function_06 endp
;****************************************************************************
; EMM Function 7 (Get Version)
;****************************************************************************
assume ds:code
emm_function_07 proc near
mov al,ems_version ;Load version number in AL
xor ah,ah ;Zero AH for return
mov bp,KEEPNONE
ret
emm_function_07 endp
;****************************************************************************
; EMM Function 8 (Save Page Map)
;****************************************************************************
assume ds:code
emm_function_08 proc near
call check_handle ;Error if DX contains an
mov ah,INVALID_HANDLE ;invalid handle
jc emm8_exit
call check_save_map ;Error if save area already
mov ah,MAP_ALREADY_SAVED ;contains a map for this
jnc emm8_exit ;handle
push dx ;Save handle
mov dx,0FFh ;Find address of first free
call check_save_map ;map in the save area
pop dx ;Retrieve handle
mov ah,SAVE_AREA_FULL ;Error if the save area
jc emm8_exit ;is full
mov di,si ;Store the handle number
mov [di],dl
inc di
mov si,offset page_map_0 ;Copy maps for pages 0
mov cx,4 ;thru 3 to the save
rep movsw ;area
xor ah,ah ;Zero AH for return
emm8_exit: mov bp,KEEPNONE
ret
emm_function_08 endp
;****************************************************************************
; EMM Function 9 (Restore Page Map)
;****************************************************************************
assume ds:code
emm_function_09 proc near
call check_handle ;Error if DX contains an
mov ah,INVALID_HANDLE ;invalid handle
jc emm9_exit
call check_save_map ;Error if no map exists
mov ah,MAP_NOT_FOUND ;for this handle
jc emm9_exit
mov byte ptr [si],0FFh ;Mark map as unused
inc si ;Advance SI to saved map
call restore_page_map ;Restore the page map
mov ah,HARDWARE_ERROR ;Exit on error
jc emm9_exit
xor ah,ah ;Zero AH for return
emm9_exit: mov bp,KEEPNONE
ret
emm_function_09 endp
;****************************************************************************
; EMM Function 10 (Reserved)
;****************************************************************************
assume ds:code
emm_function_10 proc near
mov ah,INVALID_FUNCTION
mov bp,KEEPNONE
ret
emm_function_10 endp
;****************************************************************************
; EMM Function 11 (Reserved)
;****************************************************************************
assume ds:code
emm_function_11 proc near
mov ah,INVALID_FUNCTION
mov bp,KEEPNONE
ret
emm_function_11 endp
;****************************************************************************
; EMM Function 12 (Get Handle Count)
;****************************************************************************
assume ds:code
emm_function_12 proc near
mov bl,active_handles ;Place count in BL
xor bh,bh ;Byte to word in BX
xor ah,ah ;Zero AH for return
emm12_exit: mov bp,KEEPBX
ret
emm_function_12 endp
;****************************************************************************
; EMM Function 13 (Get Handle Pages)
;****************************************************************************
assume ds:code
emm_function_13 proc near
xor bx,bx ;Exit with count equal to
or dx,dx ;0 if handle number is 0
jz emm13_1
call check_handle ;Error if DX contains an
mov ah,INVALID_HANDLE ;invalid handle
jc emm13_exit
call get_page_count ;Get page count in BX
emm13_1: xor ah,ah ;Zero AH for return
emm13_exit: mov bp,KEEPBX
ret
emm_function_13 endp
;****************************************************************************
; EMM Function 14 (Get All Handle Pages)
;****************************************************************************
assume ds:code
emm_function_14 proc near
mov bp,sp ;Restore value of ES on
mov es,[bp+2] ;entry to this function
mov ax,cs ;Point ES back to this
mov es,ax ;segment
mov dx,-1 ;Initialize handle number
mov cl,active_handles ;Initialize handle count
xor ch,ch ;Byte to word in CX
emm14_1: inc dx ;Increment handle
push cx ;Save count
push di ;Save DI
call check_handle ;See if handle in DX is valid
pop di ;Retrieve DI
pop cx ;Retrieve count
jc emm14_1 ;Branch if handle invalid
push cx ;Save count again
call get_page_count ;Get page count if valid
mov es,[bp+2] ;Point ES to user data space
mov es:[di],dx ;Store handle number
mov es:[di+2],bx ;Store page count
add di,4 ;Advance DI
mov ax,cs ;Point ES back to this
mov es,ax ;segment
pop cx ;Retrieve count
loop emm14_1 ;Loop until done
emm14_exit: jmp emm_function_12 ;Exit through function 12
emm_function_14 endp
;****************************************************************************
; EMM Function 15 (Get/Set Page Map)
;****************************************************************************
assume ds:code
emm_function_15 proc near
mov bp,sp ;Make stack addressable
mov al,byte ptr [bp+16] ;Retrieve subfunction code
mov ah,INVALID_SUBFUNCTION ;Branch if subfunction code
cmp al,0 ;is other than 0
jne emm15_1
mov bp,sp ;Restore value of ES on
mov es,[bp+2] ;entry to this function
mov si,offset page_map_0 ;Copy maps for pages 0
mov cx,4 ;thru 3 to the user's
rep movsw ;data space
xor ah,ah ;Zero AH for return
jmp short emm15_exit ;Exit
;
; Function 15, Subfunction 1.
;
emm15_1: cmp al,1 ;Branch if subfunction code
jne emm15_2 ;is other than 1
mov bp,sp ;Restore value of DS on
emm15_1_1: mov ds,[bp+4] ;entry to this function
assume ds:nothing
call restore_page_map ;Restore the page map
mov ah,HARDWARE_ERROR ;Exit on error
jc emm15_exit
xor ah,ah ;Zero AH for return
jmp short emm15_exit ;Exit
assume ds:code
;
; Function 15, Subfunction 2.
;
emm15_2: cmp al,2 ;Branch if subfunction code
jne emm15_3 ;is other than 2
mov bp,sp ;Restore value of ES on
mov es,[bp+2] ;entry to this function
push si ;Save SI
mov si,offset page_map_0 ;Copy maps for pages 0
mov cx,4 ;thru 3 to the user's
rep movsw ;data space
pop si ;Restore SI
jmp emm15_1_1 ;Branch and finish
;
; Function 15, Subfunction 3.
;
emm15_3: cmp al,3 ;Error if subfunction number
jne emm15_exit ;is other than 3
mov ax,0008h ;Prepare AX for return
emm15_exit: mov bp,KEEPNONE
ret
emm_function_15 endp
;****************************************************************************
; GET_FREE_PAGES returns the number of unallocated pages in BX. On entry,
; DS must point to the code segment.
;****************************************************************************
assume ds:code
get_free_pages proc near
mov si,offset page_table ;Point SI to page table
mov cx,pages ;Set CX to number of pages
xor bx,bx ;Zero unallocated page count
getfree1: lodsw ;Get page table entry
cmp ax,0FFFFh ;Branch if allocated
jne getfree2
inc bx ;Increment count
getfree2: add si,2 ;Skip next field in table
loop getfree1 ;Loop until done
ret
get_free_pages endp
;****************************************************************************
; GET_FREE_HANDLE returns the number of the lowest available handle in DX.
; On return, carry set means no handles are available. On entry, ES must
; point to the code segment.
;****************************************************************************
assume ds:code
get_free_handle proc near
mov cx,handles ;Place count in CX
mov al,00h ;Search the handle table
mov di,hndl_table_addr ;until an unallocated
repne scasb ;handle is found
je gethand1 ;Branch if handle found
stc ;Set carry and return
ret
gethand1: inc cx ;Compute handle number
mov dx,handles ;in DX
sub dx,cx
clc ;Clear carry and return
ret
get_free_handle endp
;****************************************************************************
; ALLOCATE_PAGES allocates the number of EMS pages in CX to the handle in
; DX and validates the handle. No checking is performed to make sure the
; number of pages requested in CX are available. On entry, DS must point
; to the code segment.
;****************************************************************************
assume ds:code
allocate_pages proc near
inc active_handles ;Increment handle count
mov si,hndl_table_addr ;Validate the handle
add si,dx
mov byte ptr [si],1
jcxz allocpage2 ;Branch if CX=0
mov bx,0 ;Initialize log page number
mov si,offset page_table ;Point SI to page table
allocpage1: lodsw ;Get an entry
add si,2 ;Skip logical page number
cmp ax,0FFFFh ;Branch if the page is
jne allocpage1 ;already allocated
mov [si-4],dx ;Write handle to table
mov [si-2],bx ;Write logical page number
inc bx ;Increment page number
loop allocpage1 ;Loop until done
allocpage2: ret
allocate_pages endp
;****************************************************************************
; RELEASE_PAGES deallocates all pages that belong to the handle in DX and
; invalidates the handle. On entry, both DS and ES must point to the code
; segment.
;****************************************************************************
assume ds:code
release_pages proc near
cmp dx,0 ;Branch if handle number
je relpage0 ;is 0
dec active_handles ;Decrement handle count
mov si,hndl_table_addr ;Invalidate the handle
add si,dx
mov byte ptr [si],0
relpage0: mov si,offset page_table ;Point SI to page table
mov cx,pages ;Initialize page count
relpage1: lodsw ;Get a page table entry
cmp ax,dx ;Branch if the page belongs
jne relpage2 ;to another handle
mov word ptr [si-2],0FFFFh ;Mark the page as unused
mov word ptr [si],0000h ;Zero the logical page number
relpage2: add si,2 ;Skip logical page number
loop relpage1 ;Loop until done
ret
release_pages endp
;****************************************************************************
; CHECK_HANDLE returns with carry clear if the handle in DX is a valid
; handle, carry set if DX does not contain a valid handle. On entry, DS
; must point to the code segment.
;****************************************************************************
assume ds:code
check_handle proc near
cmp dx,handles ;Handle invalid if number is
jae handle_bad ;greater than handle count
mov si,hndl_table_addr ;Handle invalid if corre-
add si,dx ;sponding byte in handle
cmp byte ptr [si],0 ;table is 0
je handle_bad
clc ;Clear carry and return
ret
handle_bad: stc ;Set carry and return
ret
check_handle endp
;****************************************************************************
; CHECK_SAVE_MAP returns with carry clear if the page map save area
; contains a page map for the handle in DX, carry set if it does not.
; If a handle is found, the offset address of the corresponding page
; map is returned in SI. To search for a free map in the save area,
; call this procedure with DX set to 0FFh. On entry, DS must point
; to the code segment.
;****************************************************************************
assume ds:code
check_save_map proc near
mov si,offset saved_maps ;Point SI to save area
mov cx,64 ;Initialize count in CX
csm1: cmp byte ptr [si],dl ;Check one entry
je map_found ;Branch if handle matches
add si,9 ;Point SI to next entry
loop csm1 ;Loop until done
stc ;Set carry and return
ret
map_found: clc ;Clear carry and return
ret
check_save_map endp
;****************************************************************************
; GET_PAGE_COUNT returns in BX the number of logical pages allocated to
; the handle in DX. No checking is performed to make sure the handle is
; valid. On entry, DS must point to the code segment.
;****************************************************************************
assume ds:code
get_page_count proc near
mov si,offset page_table ;Point SI to page table
mov cx,pages ;Initialize count in CX
xor bx,bx ;Initalize page count
gpc1: lodsw ;Get a page table entry
cmp ax,dx ;Increment page count if
jne gpc2 ;the handles match
inc bx
gpc2: add si,2 ;Skip logical page number
loop gpc1 ;Loop until done
ret
get_page_count endp
;****************************************************************************
; GET_ABS_PAGE returns in AX the absolute page number that corresponds to
; the handle in DX and the logical page number in BX. No checking is per-
; formed to make sure the handle and the logical page number are valid.
; On entry, DS must point to the code segment.
;****************************************************************************
assume ds:code
get_abs_page proc near
mov si,offset page_table ;Point SI to page table
mov cx,pages ;Initialize page count
gpa1: lodsw ;Get a page table entry
cmp ax,dx ;Branch if page does not
jne gpa2 ;belong to handle in DX
cmp bx,[si] ;Branch if logical page
je gpa3 ;numbers match
gpa2: add si,2 ;Skip logical page number
loop gpa1 ;Loop back for more
gpa3: mov ax,pages ;Compute absolute page
sub ax,cx ;number in AX
ret
get_abs_page endp
;****************************************************************************
; MAP_EMS_PAGE maps the absolute page whose number is passed in BX
; to the physical page whose number is passed in AL. On return, carry
; clear means the page was mapped. Carry set means an error occurred.
; On entry, DS must point to the code segment.
;****************************************************************************
EMS_PPAGE equ byte ptr [bp+2]
EMS_APAGE equ word ptr [bp]
assume ds:code
map_ems_page proc near
push bp ;Save BP
push ax ;Save page numbers on the
push bx ;stack
mov bp,sp ;Make stack addressable
mov si,offset page_map_0 ;Compute offset into page
xor ah,ah ;map array for specified
shl ax,1 ;physical page
add si,ax
mov ax,[si] ;Get absolute page number
cmp ax,0FFFFh ;Branch if the page is
je mep1 ;currently unmapped
mov bl,EMS_PPAGE ;Get physical page number
call copy_p2a ;Copy page to XMS memory
jc mep_error ;Exit on error
mep1: mov ax,EMS_APAGE ;Get absolute page number
cmp ax,0FFFFh ;Branch if page number is
je mep2 ;FFFFh
mov bl,EMS_PPAGE ;Get physical page number
call copy_a2p ;Copy page from XMS memory
jc mep_error ;Exit on error
mep2: mov si,offset page_map_0 ;Compute offset into page
mov al,EMS_PPAGE ;map array for specified
xor ah,ah ;physical page
shl ax,1
add si,ax
mov bx,EMS_APAGE ;Get absolute page number
mov word ptr [si],bx ;Store absolute page number
add sp,4 ;Clear the stack
pop bp ;Restore BP
clc ;Clear carry
ret ;Return
mep_error: add sp,4 ;Clear the stack
pop bp ;Restore BP
stc ;Set carry
ret ;Return
map_ems_page endp
;****************************************************************************
; COPY_P2A copies the physical page whose page number is passed in BL to the
; page in XMS memory whose absolute page number is passed in AX. On return,
; carry set means the operation succeeded, carry set means it did not. On
; entry, DS must point to the code segment.
;****************************************************************************
XMS_LENGTH_LO equ word ptr [bp]
XMS_LENGTH_HI equ word ptr [bp+2]
XMS_SRC_HANDLE equ word ptr [bp+4]
XMS_SRC_OFFSET_LO equ word ptr [bp+6]
XMS_SRC_OFFSET_HI equ word ptr [bp+8]
XMS_DST_HANDLE equ word ptr [bp+10]
XMS_DST_OFFSET_LO equ word ptr [bp+12]
XMS_DST_OFFSET_HI equ word ptr [bp+14]
assume ds:code
copy_p2a proc near
push bp ;Save BP
sub sp,16 ;Make room on stack
mov bp,sp ;Make stack addressable
mov XMS_LENGTH_LO,16384 ;Set block length (16K)
mov XMS_LENGTH_HI,0
mov dx,emb_handle ;Set destination handle
mov XMS_DST_HANDLE,dx
xor dx,dx ;Compute offset into EMB
mov cx,14 ;that corresponds to the
cp2a_1: shl ax,1 ;absolute page number in
rcl dx,1 ;AX
loop cp2a_1
mov XMS_DST_OFFSET_LO,ax ;Write result to XMS
mov XMS_DST_OFFSET_HI,dx ;parameter block
mov XMS_SRC_HANDLE,0 ;Set source handle
mov ax,16384 ;Compute offset into page
xor bh,bh ;frame that corresponds to
mul bx ;the physical page number
mov XMS_SRC_OFFSET_LO,ax ;Write result to XMS
mov ax,page_frame ;parameter block
mov XMS_SRC_OFFSET_HI,ax
push ds ;Save DS
mov si,bp ;Point DS:SI to XMS
mov ax,ss ;parameter block
mov ds,ax
assume ds:nothing
mov ah,0Bh ;XMS function code in AH
call cs:[xmm] ;Execute block move
pop ds ;Retrieve DS
assume ds:code
or ax,ax ;Branch on error
jz cp2a_error
add sp,16 ;Clear the stack
pop bp ;Restore BP
clc ;Clear carry
ret ;Return
cp2a_error: add sp,16 ;Clear the stack
pop bp ;Restore BP
stc ;Set carry
ret ;Return
copy_p2a endp
;****************************************************************************
; COPY_A2P copies the page in XMS memory whose page number is passed in
; AX to the physical page whose page number is passed in BL. On return,
; carry set means the operation succeeded, carry set means it did not.
; On entry, DS must point to the code segment.
;****************************************************************************
assume ds:code
copy_a2p proc near
push bp ;Save BP
sub sp,16 ;Make room on stack
mov bp,sp ;Make stack addressable
mov XMS_LENGTH_LO,16384 ;Set block length (16K)
mov XMS_LENGTH_HI,0
mov dx,emb_handle ;Set source handle
mov XMS_SRC_HANDLE,dx
xor dx,dx ;Compute offset into EMB
mov cx,14 ;that corresponds to the
ca2p_1: shl ax,1 ;absolute page number in
rcl dx,1 ;AX
loop ca2p_1
mov XMS_SRC_OFFSET_LO,ax ;Write result to XMS
mov XMS_SRC_OFFSET_HI,dx ;parameter block
mov XMS_DST_HANDLE,0 ;Set destination handle
mov ax,16384 ;Compute offset into page
xor bh,bh ;frame that corresponds to
mul bx ;the physical page number
mov XMS_DST_OFFSET_LO,ax ;Write result to XMS
mov ax,page_frame ;parameter block
mov XMS_DST_OFFSET_HI,ax
push ds ;Save DS
mov si,bp ;Point DS:SI to XMS
mov ax,ss ;parameter block
mov ds,ax
assume ds:nothing
mov ah,0Bh ;XMS function code in AH
call cs:[xmm] ;Execute block move
pop ds ;Retrieve DS
assume ds:code
or ax,ax ;Branch on error
jz ca2p_error
add sp,16 ;Clear the stack
pop bp ;Restore BP
clc ;Clear carry
ret ;Return
ca2p_error: add sp,16 ;Clear the stack
pop bp ;Restore BP
stc ;Set carry
ret ;Return
copy_a2p endp
;****************************************************************************
; RESTORE_PAGE_MAP restores the page map whose address is passed in DS:SI.
; On return, carry clear means the mapping succeeded. Carry set means an
; error occurred.
;****************************************************************************
assume ds:nothing
restore_page_map proc near
mov cx,4 ;Initialize page count
rpm_loop: lodsw ;Get absolute page number
push cx ;Save registers
push si
push ds
mov bx,cs ;Point DS back to this
mov ds,bx ;segment for now
mov bx,ax ;Transfer it to BX
mov ax,4 ;Compute physical page
sub ax,cx ;number in AX
call map_ems_page ;Map the EMS page
pop ds ;Restore registers
pop si
pop cx
jc rpm_exit ;Exit on error
loop rpm_loop ;Loop until done
clc ;Clear carry for exit
rpm_exit: ret
restore_page_map endp
;****************************************************************************
; INIT parses the DEVICE= line and initializes the device driver.
;****************************************************************************
page_table label byte
copyright db "XMS2EMS 1.1 Copyright (c) 1993 Jeff Prosise",13,10
db "From: PC Magazine DOS 6 Memory Management with "
db "Utilities",13,10,"$"
errmsg0 db "ERROR: $"
errmsg1 db "An expanded memory manager is already installed"
db 13,10,"$"
errmsg2 db "HIMEM.SYS must be installed before XMS2EMS.SYS"
db 13,10,"$"
errmsg3 db "Invalid parameter or parameter out of range"
db 13,10,"$"
errmsg4 db "XMS2EMS not installed"
db 13,10,"$"
errmsg5 db "Insufficient XMS memory available",13,10,"$"
msg1 db "Expanded memory available: $"
msg2 db "K",13,10,"$"
assume cs:code,ds:nothing
init proc near
mov ax,cs ;Point DS to code segment
mov ds,ax
mov ah,09h ;Display installation
mov dx,offset copyright ;message
int 21h
push di ;Make sure there's not
push es ;an EMM already loaded
xor ax,ax
mov ds,ax
mov es,ds:[19Eh]
mov ax,cs
mov ds,ax
assume ds:code
mov si,offset devname
mov di,10
mov cx,8
repe cmpsb
pop es
pop di
jne init2
;
; Display error message, zero the break address, and return.
;
mov dx,offset errmsg1
init_error: mov ax,cs ;Point DS to code segment
mov ds,ax
push dx ;Save message pointer
mov ah,09h ;Print "ERROR:"
mov dx,offset errmsg0
int 21h
pop dx ;Retrieve message pointer
mov ah,09h ;Print error message
int 21h
mov ah,09h ;Print "XMS2EMS not
mov dx,offset errmsg4 ;installed"
int 21h
mov word ptr es:[di+14],0 ;Zero the break address
mov word ptr es:[di+16],cs
ret
;
; Make sure HIMEM.SYS is loaded and get the driver's entry point.
;
init2: mov ax,4300h ;Make sure the driver is
int 2Fh ;present with function
mov dx,offset errmsg2 ;4300h
cmp al,80h
jne init_error
push es ;Get its entry point address
mov ax,4310h ;with function 4310h
int 2Fh
mov word ptr xmm,bx
mov word ptr xmm[2],es
pop es
;
; Parse the DEVICE= line for parameters.
;
lds si,es:[di+18] ;Point DS:SI to the DEVICE=
assume ds:nothing ;line
cld ;Clear direction flag
call finddelim ;Skip device driver name
jc init5 ;Branch if end of line
init3: call findchar ;Find the first character
jc init5 ;Branch if end of line
cmp byte ptr [si],"/" ;Branch if the character is
jne init4 ;not a "/"
inc si ;Advance past the "/"
lodsw ;Get the next two characters
and al,0DFh ;Capitalize the first one
mov dx,offset errmsg3 ;Initialize error pointer
cmp ax,3D48h ;Error if not "H="
jne init_error
call asc2bin ;Get the number following
mov dx,offset errmsg3 ;the equal sign
jc init_error
cmp ax,8 ;Check the range
jb init_error
cmp ax,255
ja init_error
mov cs:[handles],ax ;Store number of handles
jmp init3 ;Return to parsing loop
init4: call asc2bin ;Get the number of kilobytes
mov dx,offset errmsg3 ;requested
jc init_error2
cmp ax,256 ;Check the range
jb init_error2
cmp ax,8192
ja init_error2
mov cs:[memory],ax ;Store kilobytes requested
jmp init3 ;Return to parsing loop
;
; Allocate an extended memory block (EMB) from the XMS driver.
;
init5: mov ax,cs ;Point DS back to the
mov ds,ax ;code segment
assume ds:code
add memory,15 ;Round MEMORY value up to
and memory,0FFF0h ;the nearest 16K boundary
get_emb: mov ah,09h ;Request an EMB of the
mov dx,memory ;same length
call xmm
or ax,ax ;Branch if no error
jnz init7
check_emb: mov ah,08h ;Get the size of the largest
call xmm ;free EMB
mov dx,offset errmsg5 ;Error if it's smaller than
cmp ax,256 ;256K
jae init6
init_error2: jmp init_error
init6: and ax,0FFF0h ;Round down to the nearest
mov memory,ax ;16K boundary
jmp get_emb ;Loop back and try again
init7: mov emb_handle,dx ;Save EMB handle
mov ax,memory ;Calculate the number of
mov cl,4 ;pages and save the result
shr ax,cl ;for later
mov pages,ax
;
; Alter the interrupt 67h vector, compute the break address, and return.
;
mov ah,09h ;Display the size of the
mov dx,offset msg1 ;EMS memory pool
int 21h
mov ax,memory
call bin2asc
mov ah,09h
mov dx,offset msg2
int 21h
mov ax,pages ;Compute the address of
shl ax,1 ;the handle table
shl ax,1
add ax,offset page_table
mov hndl_table_addr,ax ;Save it
add ax,handles ;Compute ending address
add ax,15 ;Compute the segment address
mov cl,4 ;of the next higher para-
shr ax,cl ;graph in memory for the
mov bx,cs ;page frame address
add ax,bx
mov page_frame,ax ;Save page frame address
add ax,1000h ;Add 1000h for break address
mov word ptr es:[di+14],0 ;Store the break address in
mov word ptr es:[di+16],ax ;the request header
xor ax,ax ;Grab the INT 67h vector
mov es,ax
cli
mov word ptr es:[19Ch],offset int67h
mov word ptr es:[19Eh],cs
sti
ret
init endp
;****************************************************************************
; FINDCHAR advances SI to the next non-white-space character. On return,
; carry set indicates EOL was encountered.
;****************************************************************************
findchar proc near
lodsb ;Get the next character
cmp al,09h ;Loop if tab
je findchar
cmp al,20h ;Loop if space
je findchar
cmp al,2Ch ;Loop if comma
je findchar
dec si ;Point SI to the character
cmp al,0Dh ;Exit with carry set if end
je eol ;of line is reached
cmp al,0Ah
je eol
clc ;Clear carry and exit
ret
eol: stc ;Set carry and exit
ret
findchar endp
;****************************************************************************
; FINDDELIM advances SI to the next white-space character. On return,
; carry set indicates EOL was encountered.
;****************************************************************************
finddelim proc near
lodsb ;Get the next character
cmp al,09h ;Exit if tab
je findchar
cmp al,20h ;Exit if space
je fd_exit
cmp al,2Ch ;Exit if comma
je fd_exit
cmp al,0Ah ;Exit if line feed
je fd_eol
cmp al,0Dh ;Loop back for more if end
jne finddelim ;of line isn't reached
fd_eol: dec si ;Set carry and exit
stc
ret
fd_exit: dec si ;Clear carry and exit
clc
ret
finddelim endp
;****************************************************************************
; ASC2BIN converts a decimal number entered in ASCII form into a binary
; value in AX. Carry set on return indicates that an error occurred in
; the conversion.
;****************************************************************************
asc2bin proc near
xor ax,ax ;Initialize registers
xor bx,bx
mov cx,10
a2b_loop: mov bl,[si] ;Get a character
inc si
cmp bl,20h ;Exit if space
je a2b_exit
cmp bl,2Ch ;Exit if comma
je a2b_exit
cmp bl,0Dh ;Exit if carriage return
je a2b_exit
cmp bl,0Ah ;Exit if line feed
je a2b_exit
cmp bl,"0" ;Error if character is not
jb a2b_error ;a number
cmp bl,"9"
ja a2b_error
mul cx ;Multiply the value in AX by
jc a2b_error ;10 and exit on overflow
sub bl,30h ;Convert ASCII to binary
add ax,bx ;Add latest value to AX
jnc a2b_loop ;Loop back for more if sum
;is less than 65,535
a2b_error: dec si ;Set carry and exit
stc
ret
a2b_exit: dec si ;Clear carry and exit
clc
ret
asc2bin endp
;****************************************************************************
; BIN2ASC converts a binary value in AX to ASCII form and displays it.
;****************************************************************************
bin2asc proc near
mov bx,10 ;Initialize divisor word and
xor cx,cx ;digit counter
b2a1: inc cx ;Increment digit count
xor dx,dx ;Divide by 10
div bx
push dx ;Save remainder on stack
or ax,ax ;Loop until quotient is zero
jnz b2a1
b2a2: pop dx ;Retrieve a digit from stack
add dl,30h ;Convert it to ASCII
mov ah,2 ;Display it
int 21h
loop b2a2 ;Loop until done
ret
bin2asc endp
code ends
end